/**
 * Sybil Project Management
 * 
 * Sybil is an client/server-based application for managing projects and
 * resources.
 * 
 * Copyright (C) 2009 Natascha Chrobok <nat@sybil-pm.org>
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.sybil_pm.persistence;

import java.io.IOException;
import java.io.Reader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Calendar;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
import com.mysql.jdbc.Statement;


import javax.xml.bind.annotation.*;

public class SybilDB {
	
	/*
	 * Instance variable 
	 */
	private static SybilDB instance;
	
	private static SqlMapClient sqlMapClient;
	
	/*
	 * Location of the SqlMapConfig-file 
	 */
	private static final String configFile = "org/sybil_pm/persistence/SqlMapConfig.xml";			
		
	/*
	 * MySql-Connection 
	 *
	 * don't use this variable 
	 */
	private Connection con;
		
	/*
	 * this is just as stupid idea of mine...
	 * 	 */
	private int theNumber;
		
	/**
	 * Constructor
	 */
	private SybilDB() throws IOException {
		Reader reader = Resources.getResourceAsReader(configFile);
		sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
		reader.close();
		theNumber = 0;		
	}
	
	
	/**
	 * Returns an instance of SybilDB and makes sure that there always exists only 
	 * one copy of SybilDB.
	 * 
	 * @throws IOException if the file SqlMapConfig.xml could not be found
	 * @return working instance of SybilDB
	 */
	static public SybilDB getInstance() throws IOException {
		if (instance == null) instance = new SybilDB();
		return instance;
	}
	
	/**
	 * Generates a unique Id for a database-table. Currently this Id consists of
	 * the MD5-hashed name of the table, the string-value of a value in that table
	 * (programers-choice) and the time in miliseconds that have passed since 1.1.1970
	 * (ie. the output of Calendar.getTimeInMillis())
	 * 
	 * I'm aware that this method is relatively simple and maybe a
	 * little bit idiotic and I will replace it. Eventually I will genereate a more
	 * high sophisticated algorithm for generating a unique id. However in this early
	 * development stage of the project it is IMO not so important to have a super-
	 * duper algorithm.
	 * 
	 * 
	 * @param tableName the name of the database table the UId should be generated for
	 * @param aValue a string-value 
	 * @return String uid
	 * @todo exception-handling
	 */
	public String generateUID(String tableName, String aValue) {
		
		StringBuffer uid = new StringBuffer();
		String name = new String(tableName + aValue);
		Calendar now = Calendar.getInstance();
		
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.reset();			
			md5.update(name.getBytes());
			
			byte[] result = md5.digest(); 

			for (int i = 0; i < result.length; i++) {
				String part = Integer.toHexString(0xFF & result[i]);
				
				//toHexString truncates a leading 0, thus 
				//0xFF09 becomes 0xFF9 so in this case 
				//we have to add the 
				//leading 0 again
				if (part.length() == 1)
					uid.append("0");
				
				uid.append(part);
				}
			
		} catch (NoSuchAlgorithmException e) {
			//todo
		} finally {
			uid.append(drawNumber());
			uid.append(Long.toHexString(now.getTimeInMillis()));	
		}
		
		return uid.toString();
	}

	/**
	 * This method randomizes the generated unique id in the method generateUID
	 * a little bit. I'm aware that this method is relatively simple and maybe a
	 * little bit idiotic and I will replace it. Eventually I will genereate a more
	 * high sophisticated algorithm for generating a unique id. However in this early
	 * development stage of the project it is IMO not so important to have a super-
	 * duper algorithm.
	 * 
	 * @return a number between 0 and 9
	 */
	private int drawNumber() {
		 if (theNumber < 10) 
			 theNumber++;
		 else
			 theNumber=0;
		return theNumber;
	}
	
	
	/**
	 * This method creates a standard java.util.PreparedStatement. Since SybilServer 
	 * uses iBatis for database-access this method should not be used. 
	 * 
	 * @deprecated
	 * @param sql sql-string for the prepareStatement
	 * @return PreparedStatement
	 * @throws SQLException
	 */
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return (PreparedStatement) con.prepareStatement(sql);
	}
	
	/**
	 * This method creates a standard java.util.Statement. Since SybilServer uses
	 * iBatis for database-access this method should not be used.
	 * 
	 * @deprecated
	 * @return
	 * @throws SQLException
	 */
	public Statement createStatement() throws SQLException {
		return (Statement) con.createStatement();
	}
	
	
	/**
	 * This method connects to a (mysql) database and returns true if the connection
	 * was established. Otherwise it returns false. It was used in the early develop-
	 * ment of SybilServer, so even the database-information of the testserver is hardcoded.
	 * As it will probably not work on your machine you should even forget that this
	 * method exists. Since SybilServer uses iBatis for database-access 
	 * this method should't be used anyway. 
	 * 
	 * @deprecated
	 * @return true if connection could be established
	 * @throws SQLException
	 */
	public boolean connect() throws SQLException {
		
		try {
			Class.forName("com.mysql.jdbc.Driver");
		}
		catch (ClassNotFoundException e) {
			return false;
		}
		
		con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/sybil",
				"sybil",
				"sybil" );
				
		return true;
	}
	
	/**
	 * Returns the reference to the SqLMapClient
	 * 
	 * @return
	 */
	public SqlMapClient getSqlMapClient() {
		return sqlMapClient;
	}
}
